<template>
  <div className="px-[2vw] w-full h-full relative">
    <div className="border-b-2 mb-1">
      <v-icon :icon="mdiChevronLeft" size="50" @click="backToList" />
      <span>{{ webSocket.curChatUser?.username }}</span>
    </div>
    <!-- 消息 -->
    <div
      className="w-full overflow-scroll transition-all ease-in-out duration-300"
      :style="`height: ${emojiBox ? '50' : '79'}vh;`"
      ref="scrollContainer"
    >
      <div v-for="(m, index) in msgList" :key="index">
        <!-- 时间 -->
        <div className="text-center text-xs">
          <div
            className="rounded inline-block bg-gray-300 px-2 text-white"
            v-if="timeDiff(index).show"
          >
            {{ timeDiff(index).time }}
          </div>
        </div>
        <!-- 时间 -->
        <section className="w-full flex justify-between my-3">
          <!-- 左侧头像 -->
          <div className="size-9 flex justify-center items-center">
            <v-avatar
              :image="userInfo.getHeader(webSocket.curChatUser?.avatar)"
              size="36"
              v-if="m.fid === webSocket.curChatUser?.id.toString()"
              @click="toFriend(webSocket.curChatUser.id)"
            />
          </div>
          <!-- 左侧头像 -->
          <i
            v-if="m.fid === webSocket.curChatUser?.id.toString()"
            className="w-0 h-0 border-y-transparent border-y-4 border-r-4 border-r-blue-300 mt-3 ml-1"
          />
          <div
            className="flex-auto *:w-auto *:p-2 *:bg-blue-300 *:inline-block *:rounded *:text-sm
        "
          >
            <!-- 内容 -->
            <div
              :className="`float-${
                m.fid === webSocket.curChatUser?.id.toString()
                  ? 'left'
                  : 'right'
              }`"
              v-html="m.msg"
            />
            <!-- 内容 -->
          </div>
          <i
            v-if="m.tid === webSocket.curChatUser?.id.toString()"
            className="w-0 h-0 border-y-transparent border-y-4 border-l-4 border-l-blue-300 mt-3 mr-1"
          />
          <!-- 右侧头像 -->
          <div className="size-9 flex justify-center items-center ">
            <v-avatar
              :image="userInfo.getHeader()"
              size="36"
              v-if="m.tid === webSocket.curChatUser?.id.toString()"
            />
          </div>
          <!-- 右侧头像 -->
        </section>
      </div>
    </div>
    <!-- 底部输入框 -->
    <div
      className="absolute bottom-0 w-[96vw] h-[42vh] bg-white border-t-2 transition-all ease-in-out duration-300"
      :style="`height: ${emojiBox ? '42' : '13'}vh;`"
    >
      <v-textarea
        rows="1"
        v-model="msg"
        hide-details
        :clearable="true"
        @click="emojiBox = false"
        ref="textarea"
      >
        <template v-slot:append-inner>
          <v-icon :icon="mdiEmoticonHappyOutline" @click.stop="openEmojiBox" />
        </template>
      </v-textarea>
      <!-- <div v-show="!emojiBox">
        <span>Enter 发送消息；</span><span>Ctrl+Enter 换行</span>
      </div> -->
      <!-- 表情包 -->
      <div className="w-full h-[34vh]" v-show="emojiBox">
        <v-tabs v-model="tab">
          <v-tab :value="i.key" v-for="i in emojiList" :key="i.key">{{
            i.title
          }}</v-tab>
        </v-tabs>
        <v-tabs-window v-model="tab">
          <v-tabs-window-item
            v-for="j in emojiList.map((x) => x.key)"
            :key="j"
            :value="j"
          >
            <div className="grid grid-cols-8 overflow-scroll w-full h-[25vh]">
              <div
                v-for="(e, index) in emojiList[tab - 1].list"
                :key="index"
                className="text-2xl flex justify-center"
                @click="addEmoji(e.value)"
              >
                {{ e.value }}
              </div>
            </div>
          </v-tabs-window-item>
        </v-tabs-window>
      </div>
      <!-- 表情包 -->
    </div>
  </div>

  <friendInfo ref="friendBar" />
</template>

<script lang="ts" setup>
import { mdiChevronLeft, mdiEmoticonHappyOutline } from "@mdi/js";
import { useSocketInfoStore } from "@/store/webSocket";
import { useRouter } from "vue-router";
import {
  nextTick,
  onMounted,
  onUnmounted,
  reactive,
  ref,
  toRaw,
  watch,
} from "vue";
import { useUserInfoStore } from "@/store/userInfo";
import { useIndexDB } from "@/store/indexedDB";
import dayjs from "dayjs";
import { uMsg } from "@/store/vMessage";
import { emotion, hander, other } from "@/utils/emoji";

const textarea = ref();
const vMsg = uMsg();
const router = useRouter();
const webSocket = useSocketInfoStore();
const userInfo = useUserInfoStore();
const DB = useIndexDB();
const friendBar = ref();

// 消息类型
interface msgType {
  fid: string;
  tid: string;
  msg: string;
  time: number;
}

const scrollContainer = ref();

// 消息列表
const msgList = reactive<Array<msgType>>([]);

// 输入框消息
const msg = ref("");
// 表情包弹框
const emojiBox = ref<boolean>(false);
// 表情包tab
const tab = ref(1);

// 表情包内容
const emojiList = [
  {
    key: 1,
    title: "表情",
    list: emotion,
  },
  {
    key: 2,
    title: "手势",
    list: hander,
  },
  {
    key: 3,
    title: "其他",
    list: other,
  },
];

// 打开表情包
const openEmojiBox = () => {
  emojiBox.value = true;
  // 丝滑滚动
  let time = setInterval(() => {
    scrollToBottom();
  }, 1);
  setTimeout(() => {
    clearInterval(time);
  }, 300);
};

// 退出页面
const backToList = () => {
  webSocket.curChatUser = null;
  router.back();
};

// 添加表情
const addEmoji = (val: string) => {
  msg.value += val;
};

// 发送消息
const sendMsg = () => {
  // if (!emojiBox.value) {
  //   // 去掉最后一个换行符
  //   msg.value = msg.value.substring(0, msg.value.length - 1);
  // }

  // 校验信息内容
  if (!msg.value || isAllSpaces(msg.value)) {
    vMsg.Open("消息不能为空！");
    msg.value = "";
    return;
  }

  let res = "<p>";
  // 转换文本内容
  for (let i = 0; i < msg.value.length; i++) {
    switch (msg.value.charCodeAt(i)) {
      case 32: // 空格
        res += "&nbsp";
        break;
      case 10: // 换行
        res += "</p><p>";
        break;
      default:
        res += msg.value[i];
        break;
    }
  }
  res += "</p>";
  let msgObj = {
    fid: userInfo.user.id.toString(),
    tid: webSocket.curChatUser!.id.toString(),
    msg: res,
    time: new Date().getTime(),
    status: 1,
  };
  msgList.push(msgObj);
  // 保存到数据库
  let key = userInfo.user.id + ":" + webSocket.curChatUser!.id;
  DB.addOrUpdateMsg(key, {
    key: key,
    data: [...toRaw(msgList)],
  });

  reactive(msgList);

  // 发送到服务器
  webSocket.sendMsg(msgObj);

  msg.value = "";

  nextTick(() => {
    scrollToBottom();
  });
};

// 检测是否全是空格
const isAllSpaces = (str: string) => {
  for (let i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) !== 32) {
      return false;
    }
  }
  return true;
};

// 计算两个消息之间的时间差
const timeDiff = (index: number) => {
  let res = {
    time: "",
    show: false,
  };
  if (index === 0) return res;
  let before = dayjs(msgList[index - 1].time);
  let now = dayjs(msgList[index].time);
  // 时间差
  const timeDiff = now.diff(before, "s");
  // 间隔超过三分钟提示时间
  if (timeDiff > 180) {
    res.show = true;
    res.time = dayjs(now).format("HH:mm");
  }
  return res;
};

// 滚动到底部
const scrollToBottom = () => {
  scrollContainer.value &&
    (scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight);
};

onMounted(async () => {
  // 获取本地历史读取记录
  let key = userInfo.user.id + ":" + webSocket.curChatUser!.id;
  let res = await DB.getData(key);
  res && msgList.push(...res.data);
  res &&
    res.data.length > 0 &&
    res.data.forEach((i: { status: number }) => (i.status = 1));
  // 将未读的历史记录设置为已读
  res?.data &&
    DB.addOrUpdateMsg(key, {
      key: key,
      data: [...res.data],
    });
  nextTick(() => {
    scrollToBottom();
  });

  // 监听回车键事件
  document.addEventListener("keydown", KeyboardEvent);
});

// 键盘事件回调
const KeyboardEvent = (e: KeyboardEvent) => {
  if (e.key === "Enter") {
    if (!e.ctrlKey) sendMsg(); // 没有换行，发送消息
    else {
      msg.value += "\n";
    } // 有换行，换行
  }
};

// 查看好友信息
const toFriend = (id: number) => {
  friendBar.value.getUserInfo(id);
};

onUnmounted(() => {
  // 移除键盘事件和定时任务
  document.removeEventListener("keydown", KeyboardEvent);
});

// 监听webSocket是否有新消息
watch(
  () => webSocket.msg,
  (val) => {
    val.fid = val.fid.toString();
    let msgObj = {
      fid: val.fid.toString(),
      tid: userInfo.user.id.toString(),
      msg: val.msg,
      time: val.time,
      status: 1,
    };
    // 显示到页面
    msgList.push(msgObj);
    // 保存到数据库
    let key = userInfo.user.id + ":" + webSocket.curChatUser!.id;
    DB.addOrUpdateMsg(key, {
      key: key,
      data: [...toRaw(msgList)],
    });
    reactive(msgList);
    nextTick(() => {
      scrollToBottom();
    });
  }
);
</script>
